Jx.declare('Jx.event.Dispatcher', {

	$accessors: {

		'observable': undefined,

		'bubbleTarget': undefined

	},

	$helpers: {

		'Str': 'Jx.lib.String',

		'Event_Mgr': 'Jx.event.Manager'

	},

	$actions: [

		'listen',

		'unlisten',

		'purgeListeners',

		'dispatch',

		'createEventObject'

	],

	__ref_ListenerClassNs: 'event.listener',

	__ref_DefaultListenerClass: 'event.listener.default',

	__help_EachConfiguredListener: function(o, listeners, callback){

		for(var config, className, J = Jx, l = listeners.length; l--;){

			config = listeners[l];

			className = this.resolveListenerClass(config[0]);

			if (className){

				this[callback](o, className, config);

			}

			J.clearObject(config[3]);

			J.clearObject(config);

			J.clearObject(listeners);

		}

	},

	events: void(0),

	constructor: function( observable ){

		this.events = {};

		this.setObservable( observable );

		this.$super(arguments);

	},

	onDestroy: function(){

		this.purgeListeners();

		this.$super();

	},

	allowSetObservable: function(o, observable){

		o.valid = Jx.instanceOf(observable, 'Jx.event.Observable') && !observable.destroyed;

	},

	allowSetBubbleTarget: function(o, observable){

		this.allowSetObservable(o, observable);

		if (o.valid){

			o.valid = observable !== this.getObservable();

		}

	},

	allowListen: function(o){

		var params = o.params,

			args = params.slice(1),

			listeners = this.__Event_Mgr.parseListeners(args);

		args.splice(0, args.length);

		params[1] = listeners;

		o.valid = !!listeners.length;

	},

	onListen: function(o, listeners){

		this.__help_EachConfiguredListener(o, listeners, 'onApplyListener');

	},

	onApplyListener: function(o, className, config){

		var events = this.events,

			name = config[0], listener;

		if (!events.hasOwnProperty(name)){

			events[name] = Jx.instantiate(className, [ this ] );

		}

		(

			listener = o.currentListener = events[name]

		).addHandler.apply(listener, config.slice(1));

		o.currentHandler = config;

	},

	allowUnlisten: function(o){

		var params = o.params,

			args = params.slice(1),

			listeners = this.__Event_Mgr.parseListeners(args);

		args.splice(0, args.length);

		params[1] = listeners;

		o.valid = !!listeners.length;

	},

	onUnlisten: function(o, listeners){

		this.__help_EachConfiguredListener(o, listeners, 'onUnapplyListener');

	},

	onUnapplyListener: function(o, className, config){

		var events = this.events,

			name = config[0],

			found, listener;

		o.currentListener = void(0);

		o.foundHandler = false;

		if (events.hasOwnProperty(name)){

			o.foundHandler = (

				listener = o.currentListener = events[name]

			).removeHandler( config[1], config[2] );

		}

	},

	onPurgeListeners: function(o){

		console.log('purged!');

		Jx.each(this.events, function(n, v, o){

			if (o.hasOwnProperty(n)){

				v.destroy();

				delete o[n];

			}

		}, this);

	},

	allowDispatch: function(o, eventObject){

		var J = Jx;

		if (J.isString(eventObject)){

			eventObject = this.createEventObject(eventObject);

		}

		o.valid = J.instanceOf(eventObject, 'Jx.event.Operation');

		o.params[1] = eventObject;

		o.returnValue = true;

		o.execBefore = o.execAfter = true;

	},

	onBeforeDispatch: function(o, eventObject){

		// get listener
		var listeners = this.events,

			name = eventObject.getType();

		o.params[2] = o.listener = listeners.hasOwnProperty(name) ?

			listeners[name]

			:

			void(0);

	},

	onDispatch: function(o, eventObject, listener){

		//-- run listener
		if (listener){

			listener.exec(eventObject);

		}

		//-- bubble target
		if (!eventObject.destroyed){

			var observable = eventObject.getNextInPropagateChain();

			if (observable){

				observable.event.dispatch(eventObject);

			}

			o.returnValue = eventObject.getReturnValue();

		}



	},

	allowCreateEventObject: function(o, name){

		o.returnValue = void(0);

		o.valid = Jx.isString(name);

	},

	onCreateEventObject: function(o, name){

		var eventObject = o.returnValue = this.__Event_Mgr.addOperation(name);

		eventObject.setTarget( this.getObservable() );

	},

	resolveListenerClass: function(name){

		var J = Jx;

		return J.resolveClassName(

			J.isString(name) && this.__ref_ListenerClassNs+'.'+name,

			this.__ref_DefaultListenerClass

		);

	}



});
